﻿using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

// Assemblies in the Resources folder
using ServiceStack;
using ServiceStack.Text;
using SimpleOAuth;


namespace StackmobExportAWSImport
{
    public partial class StackmobExporter : Form
    {
        private AWSImporter ai;

        public StackmobExporter()
        {
            InitializeComponent();
            SetDevelopmentKeys();
            
            // Embed 3rd pary DLLs into one assembly
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

            // *****************************************
            //Uncomment to launch the child form only
            //menuToolStripMenuItem_Click(null, null);
            //WindowState = FormWindowState.Minimized;
            //ShowInTaskbar = false;
            // *****************************************
        }

        #region private functions

        /// <summary>
        /// Make a request to stackmob and receive the response
        /// </summary>
        /// <param name="address"></param>
        private void StackmobWebRequestQuery(Uri address)
        {
            txtResult.Text = string.Empty;
            txtSettings.Text = string.Empty;

            HttpWebResponse response = null;
            if (address == null) { throw new ArgumentNullException("address"); }

            try
            {
                // Create and initialize the web request  
                var request = WebRequest.Create(address) as HttpWebRequest;
                if (request == null) return;

                AddRequestHeaders(request);
                PrintRequestSetting(request);

                // add default proxy settings if behind a proxy server
                if (chkProxy.Checked)
                {
                    var proxy = request.Proxy;
                    if (proxy != null)
                    {
                        var proxyuri = proxy.GetProxy(request.RequestUri).ToString();
                        request.UseDefaultCredentials = true;
                        request.Proxy = new WebProxy(proxyuri, false)
                            {
                                Credentials = CredentialCache.DefaultCredentials
                            };
                    }
                }

                // Get response  from stackmob
                response = request.GetResponse() as HttpWebResponse;
                if (request.HaveResponse && response != null)
                {
                    // Get the response stream  
                    var resStream = response.GetResponseStream();

                    if (resStream != null)
                    {
                        using (var reader = new StreamReader(resStream))
                        {
                            // Read it into a StringBuilder  
                            var sbJSONSource = new StringBuilder(reader.ReadToEnd());
                            txtResult.Text = sbJSONSource.ToString();

                            if (chkExport.Checked)
                                WriteJsonToTxtFile(sbJSONSource.ToString());

                            if (chkcsv.Checked)
                                WriteJsonToCsvFile(sbJSONSource.ToString());
                        }
                    }
                }
            }
            catch (WebException wex)
            {
                // This exception will be raised if the server didn't return 200 - OK  
                if (wex.Response == null) return;

                using (var errorResponse = (HttpWebResponse)wex.Response)
                {
                    txtResult.Text = string.Format("The server returned '{0}' with the status code {1} ({2:d}).",
                                                   errorResponse.StatusDescription, errorResponse.StatusCode,
                                                   errorResponse.StatusCode);
                }
            }
            catch (Exception ex)
            {
                txtResult.Text = ex.Message;

                if (ex.InnerException != null)
                    txtResult.Text += " - Inner Exeption: " + ex.Message;
            }
            finally
            {
                if (response != null) { response.Close(); }
            }

            MessageBox.Show("All done!", "Complete", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void WriteJsonToTxtFile(string fileJSONOutput)
        {
            try
            {
                var prompt = MessageBox.Show("Would you like to write the JSON result to a single file?" + Environment.NewLine + 
                    Environment.NewLine + "Select 'No' to separate the keys / value count into different files?", "JSON File Result", 
                    MessageBoxButtons.YesNoCancel);

                if (prompt == DialogResult.Yes)
                {
                    var filePath = string.Format("{0}\\{1}.txt", Application.StartupPath, txtURL.Text);

                    using (var file = new StreamWriter(filePath))
                    {
                        file.Write(fileJSONOutput);
                        file.Close();
                    }
                }
                else if (prompt == DialogResult.No)
                {
                    var jsonArray = JsonObject.ParseArray(fileJSONOutput);

                    if (jsonArray.Count < 1) return;

                    var itemCount = jsonArray.Select(x => x.Count).Distinct().ToList();

                    foreach (var i in itemCount)
                    {
                        var filePath = string.Format("{0}\\{1}_{2}_Keys.txt", Application.StartupPath, txtURL.Text, i);

                        using (var file = new StreamWriter(filePath))
                        {
                            var jsonBuilder = new StringBuilder();

                            foreach (JsonObject jsonArrayObject in jsonArray)
                            {
                                if (jsonArrayObject.Count != i) continue;

                                try
                                {
                                    jsonBuilder.Append(jsonArrayObject.ToJson()).Append(",");
                                }
                                catch (Exception ex)
                                {
                                    txtResult.Text = "There was an issue adding a row(s) to the txt file. " + ex.Message;
                                }
                            }

                            if (jsonBuilder.Length > 1)
                                jsonBuilder.Remove(jsonBuilder.Length - 1, 1);

                            file.Write(jsonBuilder);
                            file.Close();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                txtResult.Text = "Could not write txt file. " + ex.Message;
            }
        }

        private void WriteJsonToCsvFile(string fileJSONOutput)
        {
            try
            {
                var jsonArray = JsonObject.ParseArray(fileJSONOutput);

                if (jsonArray.Count < 1) return;

                var itemCount = jsonArray.Select(x => x.Count).Distinct().ToList();

                foreach (var i in itemCount)
                {
                    var filePath = string.Format("{0}\\{1}_{2}_Columns.csv", Application.StartupPath, txtURL.Text, i);
                    var key = string.Empty;

                    using (var file = new StreamWriter(filePath))
                    {
                        foreach (var jsonArrayObject in jsonArray)
                        {
                            if (jsonArrayObject.Count != i) continue;

                            try
                            {
                                var keyCheck = string.Join(",", Enumerable.ToArray(jsonArrayObject.Keys));

                                if (keyCheck != key)
                                {
                                    key = keyCheck;
                                    file.WriteLine(key);
                                }

                                file.WriteLine(string.Join(",", Enumerable.ToArray(jsonArrayObject.Values)));
                            }
                            catch (Exception ex)
                            {
                                txtResult.Text = "There was an issue adding a row(s) to the CSV file. " + ex.Message;
                            }
                        }

                        file.Close();
                    }
                }
            }
            catch (Exception ex)
            {
                txtResult.Text = "Could not write CSV file. " + ex.Message;
            }
        }

        private void PrintRequestSetting(HttpWebRequest request)
        {
            txtSettings.Text = "Header Settings:";
            txtSettings.Text += Environment.NewLine + "ContentType: " + request.ContentType;
            txtSettings.Text += Environment.NewLine + "UserAgent: " + request.UserAgent;
            txtSettings.Text += Environment.NewLine + "Method: " + request.Method;
            txtSettings.Text += Environment.NewLine + "Accept: " + request.Accept;
            txtSettings.Text += Environment.NewLine + "KeepAlive: " + request.KeepAlive + " Timeout: " + request.Timeout;
            txtSettings.Text += Environment.NewLine + "AuthTokens: " + request.Headers["Authorization"];
        }

        /// <summary>
        /// <see>
        ///   <cref>https://dashboard.stackmob.com/data/browser/user?avn=0&amp;oauthvn=One</cref>
        /// </see>
        /// </summary>
        /// <param name="request"></param>
        private void AddRequestHeaders(HttpWebRequest request)
        {
            // Add request headers
            request.ContentType = "application/json; charset=UTF-8";
            request.UserAgent = "StackMob .Net Exporter";
            request.Headers.Add("X-StackMob-User-Agent", "StackMob .Net Exporter");
            request.Method = "GET";

            var version = string.Format("version={0}", rdoProd.Checked ? "1" : "0");
            request.Accept = "application/vnd.stackmob+json; " + version;

            request.KeepAlive = false;
            // Set timeout to 10 seconds
            if (string.IsNullOrEmpty(txtTimeout.Text)) txtTimeout.Text = "10";
            request.Timeout = (int.Parse(txtTimeout.Text)) * 1000;

            //OAuth version One
            request.SignRequest(new Tokens
            {
                ConsumerKey = txtKey.Text,
                ConsumerSecret = txtSecret.Text
            }).InHeader();
        }

        private void SetDevelopmentKeys()
        {
            txtKey.Text = "AddDevelopmentKey";
            txtSecret.Text = "AddDevelopmentSecret";
        }

        private void SetProductionKeys()
        {
            txtKey.Text = "AddProductionKey";
            txtSecret.Text = "AddProdcutionSecret";
        }

        #endregion

        #region Events

        /// <summary>
        /// This is used to embed the external dll added to the resources section into the StackmobExport.exe
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            var dllName = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll", "");
            dllName = dllName.Replace(".", "_");
            if (dllName.EndsWith("_resources")) return null;
            var rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", Assembly.GetExecutingAssembly());
            var bytes = (byte[])rm.GetObject(dllName);
            return Assembly.Load(bytes);
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            txtResult.Text = string.Empty;
            txtSettings.Text = string.Empty;
            rdoDev.Checked = true;
            rdoProd.Checked = false;
            txtTimeout.Text = "10";
            chkExport.Checked = false;
            SetDevelopmentKeys();
        }

        private void BtnSubmitClick(object sender, EventArgs e)
        {
            var restUri = new Uri("http://api.stackmob.com/" + txtURL.Text.Trim());
            StackmobWebRequestQuery(restUri);
        }

        private void rdoProd_CheckedChanged(object sender, EventArgs e)
        {
            SetProductionKeys();
        }

        private void rdoDev_CheckedChanged(object sender, EventArgs e)
        {
            SetDevelopmentKeys();
        }

        private void menuToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // launch the AWS importer
            if (ai == null || ai.IsDisposed)
            {
                ai = new AWSImporter(this);
            }
            ai.Show();
            Hide();
        }

        private void aboutToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            var about = new AboutBox();
            about.ShowDialog();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var readme = new StackmobReadMe();
            readme.ShowDialog();
        }

        #endregion
    }
}